# 
# Copyright (C) 2008, Brian Tanner
# 
#http://rl-glue-ext.googlecode.com/
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

import random
import sys
import copy
from state import state
from rlglue.agent.Agent import Agent
from rlglue.agent import AgentLoader as AgentLoader
from rlglue.types import Action
from rlglue.types import Observation
from random import Random
from rlglue.utils import TaskSpecVRLGLUE3

class skeleton_agent(Agent):
    agenda = 0 # Eagle
    strategies=['DFS','BFS','IDS','UCS','ASS']
    strategyIndex = -1
    searchQ=[]
    partialStateNodes=[]
    pathToGoal=[] 
    expandedStates = []
    pathToGoalIndex = 0 #Path to goal array
    
    def agent_init(self,taskSpec):
        TaskSpec = TaskSpecVRLGLUE3.TaskSpecParser(taskSpec)
        if TaskSpec.valid:
            print "valid task spec"

        else:
            print "Task Spec could not be parsed: "+taskSpec;
        print 'Task Specs'
        print '------------'
        print 'Version = ' + TaskSpec.getVersion()
        print 'Actions = ' + TaskSpec.getActions()
        print 'Char count obs = ' + str(TaskSpec.getCharCountObservations())
        print 'Int Observations = ' + str(TaskSpec.getIntObservations())
        print 'Observations = ' + TaskSpec.getObservations()
        print 'Reward range = ' + str(TaskSpec.getRewardRange())
        print '---------------------------------------------------------------'
        self.lastAction=Action()
        self.lastObservation=Observation()
        
    def compare(self,nodes,toCompare):
        print' \n -----------in Compare------------ \n'
        print len(nodes)
        print 'x %d y %d o %s G %d P %d W %d Ar %d gG %d' % (toCompare.xPos,toCompare.yPos,toCompare.orientation,toCompare.hasGold,toCompare.hasPit,toCompare.hasWumpus,toCompare.hasArrow,toCompare.grabGold)
        for x in nodes:
            print 'x %d y %d o %s G %d P %d W %d Ar %d gG %d' % (x.xPos,x.yPos,x.orientation,x.hasGold,x.hasPit,x.hasWumpus,x.hasArrow,x.grabGold)
            if x.xPos == toCompare.xPos and x.yPos == toCompare.yPos and x.orientation == toCompare.orientation:
                print 'step 1 compare'
                if  x.hasGold == toCompare.hasGold and x.hasPit == toCompare.hasPit and x.hasWumpus == toCompare.hasWumpus:
                    print 'step 2 compare'
                    if x.hasArrow == toCompare.hasArrow and x.grabGold==toCompare.grabGold:
                        print '-------------------------------------FOUND REPEATED STATE-----------------------------------------------------'
                return True
        return False
                
#    def updateStateNodes(self, Ob):
##        print len(self.partialStateNodes)
#        for i in self.partialStateNodes:
##            print Ob
#            myList = Ob.tolist()
#            i.hasGold = myList.pop(0)
#            i.hasPit = myList.pop(0)
#            i.hasWumpus = myList.pop(0)
#            if i.hasPit == True:
#                self.partialStateNodes.remove(i)
#        return self.partialStateNodes  
     
                    
    def updateStateNodes (self, Ob):
#        print len(self.partialStateNodes)
        for i in self.partialStateNodes:
#            print Ob
            myList = Ob.tolist()
            i.hasGold = myList.pop(0)
            i.hasPit = myList.pop(0)
            i.hasWumpus = myList.pop(0)
#           print i.xPos, i.yPos , i.action, i.hasGold, 'abt to grab',i.grabGold, i.orientation,'\n'
            if  i.hasPit == True:
                self.partialStateNodes.remove(i)
                break;
            if i.hasWumpus == True and i.parent.hasArrow == True and i.action == 'f':
                i.killWumpus = True;
            if i.action=='a' and i.parent.hasArrow == True:
                i.hasArrow = False
                print 'killllllllll wumpusssssssssssssss'
            if i.hasGold == True:
                if i.parent.grabGold== False and i.action=='g':
                    print 'grabbed gold'
                    i.grabGold=True
                    self.searchQ=[]
                    self.partialStateNodes=[]
                    self.partialStateNodes.append(i)
        return self.partialStateNodes    
    
    def enqueue(self,Nodes):
        x = self.strategies[self.strategyIndex]
#        print self.strategyIndex
#        print 'strategyIndex ',x,'\n'
#        for n in Nodes:
#            print n.xPos,n.yPos
        if x == 'BFS':
#            print self.searchQ
#            print 'before enqueue %d \n'% len(self.searchQ)
            self.searchQ.extend(Nodes)
#            for n in self.searchQ:
#                print n.xPos,n.yPos
#            print len(self.searchQ)
#            print 'after enqueue %d \n'% len(self.searchQ)
        if x == 'DFS':
#            print self.searchQ
#            print 'before enqueue %d \n'% len(self.searchQ)
            #self.searchQ=temp.extend(self.searchQ)
            for n in self.searchQ:
                Nodes.append(n)
            self.searchQ=Nodes  
#            for n in self.searchQ:
#                print n.xPos,n.yPos              
#            print len(self.searchQ)
#            print 'after enqueue %d \n'% len(self.searchQ)
        if x == 'UCS':
            print 'in UCS'
            #last_Q=self.searchQ[len(self.searchQ)-1]
            for n in Nodes:
                count=0
                for q in self.searchQ:
                    if n.cost <= q.cost:
                        self.searchQ.insert(count, n)
                        break
                    else:
                        count=count+1
                self.searchQ.append(n)
            print len(self.searchQ)
            for z in self.searchQ:
                print z.cost
            print 'out of UCS'
        if x == 'IDS':
            print 'IDS'
            print self.searchQ
            print 'before enqueue %d'% len(self.searchQ)
            for i in range(0, self.limit):
                for l in range(0,i):
                    for n in self.searchQ:
                        Nodes.append(n)
            self.searchQ=Nodes         
        #continue rest    
        
    def Goal(self,Node):
        #Goal Test
        if Node.grabGold == True:
            if Node.xPos == 0 and Node.yPos == 0:
                return True
        return False
    
    def createPathToGoal(self,Node):
        #Backtracking through Parents
        start = True
#        print 'create Path to Goal'
        x=Node
#        print x.xPos,x.yPos,x.orientation
        if x.xPos==0 and x.yPos==0 and x.orientation=='n':
            start=False
        while start:
#            print 'in loop'
#            print x.xPos,x.yPos,x.orientation
#            print x.action
            self.pathToGoal.insert(0,x.action)
            x=x.parent
            if x.xPos==0 and x.yPos== 0 and x.orientation=='n':
                start=False
            
        self.pathToGoal.extend('c')
        self.pathToGoal.extend('x')
    
    def getSuccessor(self,Node):
        #get the children of the node
        #Take in consideration repeated node
        print '\n in Successor'
        succesors = []
        
        if Node.killWumpus:
            fchild = state(Node.orientation ,Node,Node.xPos,Node.yPos,'a' ,False, False, False,Node.hasArrow,Node.ArrowOrien,Node.cost+1,Node.grabGold, Node.killWumpus)
            succesors.append(fchild)
        if Node.grabGold:
            
            if Node.orientation == 's':
                if Node.yPos > 0 :
                    fchild = state('s' ,Node,Node.xPos,Node.yPos-1,'f' ,False, False, False,Node.hasArrow,Node.ArrowOrien,Node.cost+1,Node.grabGold, Node.killWumpus)
                    succesors.append(fchild)
                fchild = state('w' ,Node,Node.xPos,Node.yPos,'r' ,False, False, False,Node.hasArrow,Node.ArrowOrien,Node.cost+1,Node.grabGold, Node.killWumpus)
                succesors.append(fchild)
                fchild = state('e' ,Node,Node.xPos,Node.yPos,'l' ,False, False, False,Node.hasArrow,Node.ArrowOrien,Node.cost+1,Node.grabGold, Node.killWumpus)        
                succesors.append(fchild)
                if Node.hasArrow == True:
                    fchild = state('s' ,Node,Node.xPos,Node.yPos,'a' ,False, False, False,Node.hasArrow,Node.ArrowOrien,Node.cost+1,Node.grabGold, Node.killWumpus)
                    succesors.append(fchild)                                
            if Node.orientation == 'w':
                if Node.xPos > 0 :
                    fchild = state('w' ,Node,Node.xPos-1,Node.yPos,'f' ,False, False, False,Node.hasArrow,Node.ArrowOrien,Node.cost+1,Node.grabGold, Node.killWumpus)
                    succesors.append(fchild)
                fchild = state('s' ,Node,Node.xPos,Node.yPos,'l' ,False, False, False,Node.hasArrow,Node.ArrowOrien,Node.cost+1,Node.grabGold, Node.killWumpus)        
                succesors.append(fchild)
                fchild = state('n' ,Node,Node.xPos,Node.yPos,'r' ,False, False, False,Node.hasArrow,Node.ArrowOrien,Node.cost+1,Node.grabGold, Node.killWumpus)
                succesors.append(fchild)
                if Node.hasArrow == True:
                    fchild = state('s' ,Node,Node.xPos,Node.yPos,'a' ,False, False, False,Node.hasArrow,Node.ArrowOrien,Node.cost+1,Node.grabGold, Node.killWumpus)
                    succesors.append(fchild)
            if Node.orientation == 'e':
                fchild = state('s' ,Node,Node.xPos,Node.yPos,'r' ,False, False, False,Node.hasArrow,Node.ArrowOrien,Node.cost+1,Node.grabGold, Node.killWumpus)
                succesors.append(fchild)
                if Node.xPos < 5 :
                    fchild = state('e' ,Node,Node.xPos+1,Node.yPos,'f' ,False, False, False,Node.hasArrow,Node.ArrowOrien,Node.cost+1,Node.grabGold, Node.killWumpus)
                    succesors.append(fchild)
                fchild = state('n' ,Node,Node.xPos,Node.yPos,'l' ,False, False, False,Node.hasArrow,Node.ArrowOrien,Node.cost+1,Node.grabGold, Node.killWumpus)        
                succesors.append(fchild)
                if Node.hasArrow == True:
                    fchild = state('e' ,Node,Node.xPos,Node.yPos,'a' ,False, False, False,Node.hasArrow,Node.ArrowOrien,Node.cost+1,Node.grabGold, Node.killWumpus)
                    succesors.append(fchild)    
            if Node.orientation == 'n':
                fchild = state('w' ,Node,Node.xPos,Node.yPos,'l' ,False, False, False,Node.hasArrow,Node.ArrowOrien,Node.cost+1,Node.grabGold, Node.killWumpus)        
                succesors.append(fchild)
                fchild = state('e' ,Node,Node.xPos,Node.yPos,'r' ,False, False, False,Node.hasArrow,Node.ArrowOrien,Node.cost+1,Node.grabGold, Node.killWumpus)
                succesors.append(fchild)
                if Node.yPos < 5 :
                    fchild = state('n' ,Node,Node.xPos,Node.yPos+1,'f' ,False, False, False,Node.hasArrow,Node.ArrowOrien,Node.cost+1,Node.grabGold, Node.killWumpus)
                    succesors.append(fchild)
                if Node.hasArrow == True: 
                    fchild = state('n' ,Node,Node.xPos,Node.yPos,'a' ,False, False, False,Node.hasArrow,Node.ArrowOrien,Node.cost+1,Node.grabGold, Node.killWumpus)
                    succesors.append(fchild)    
        else:
            fchild = state(Node.orientation,Node,Node.xPos,Node.yPos,'g' ,False, False, False,Node.hasArrow,Node.ArrowOrien,Node.cost+1,Node.grabGold, Node.killWumpus)
            succesors.append(fchild)
            
            if Node.orientation == 'n':
                if Node.yPos < 5 :
                    fchild = state('n' ,Node,Node.xPos,Node.yPos+1,'f' ,False, False, False,Node.hasArrow,Node.ArrowOrien,Node.cost+1,Node.grabGold, Node.killWumpus)
                    succesors.append(fchild)    
                fchild = state('e' ,Node,Node.xPos,Node.yPos,'r' ,False, False, False,Node.hasArrow,Node.ArrowOrien,Node.cost,Node.grabGold, Node.killWumpus)
                succesors.append(fchild)
                fchild = state('w' ,Node,Node.xPos,Node.yPos,'l' ,False, False, False,Node.hasArrow,Node.ArrowOrien,Node.cost,Node.grabGold, Node.killWumpus)        
                succesors.append(fchild)
                if Node.hasArrow == True:
                    fchild = state('n' ,Node,Node.xPos,Node.yPos,'a' ,False, False, False,Node.hasArrow,Node.ArrowOrien,Node.cost+1,Node.grabGold, Node.killWumpus)
                    succesors.append(fchild)
            if Node.orientation == 'e':
                if Node.xPos < 5 :
                    fchild = state('e' ,Node,Node.xPos+1,Node.yPos,'f' ,False, False, False,Node.hasArrow,Node.ArrowOrien,Node.cost+1,Node.grabGold, Node.killWumpus)
                    succesors.append(fchild)
                fchild = state('n' ,Node,Node.xPos,Node.yPos,'l' ,False, False, False,Node.hasArrow,Node.ArrowOrien,Node.cost+1,Node.grabGold, Node.killWumpus)        
                succesors.append(fchild)    
                fchild = state('s' ,Node,Node.xPos,Node.yPos,'r' ,False, False, False,Node.hasArrow,Node.ArrowOrien,Node.cost+1,Node.grabGold, Node.killWumpus)
                succesors.append(fchild)
                if Node.hasArrow == True:
                    fchild = state('e' ,Node,Node.xPos,Node.yPos,'a' ,False, False, False,Node.hasArrow,Node.ArrowOrien,Node.cost+1,Node.grabGold, Node.killWumpus)
                    succesors.append(fchild) 
            if Node.orientation == 'w':
                fchild = state('n' ,Node,Node.xPos,Node.yPos,'r' ,False, False, False,Node.hasArrow,Node.ArrowOrien,Node.cost+1,Node.grabGold, Node.killWumpus)
                succesors.append(fchild)
                fchild = state('s' ,Node,Node.xPos,Node.yPos,'l' ,False, False, False,Node.hasArrow,Node.ArrowOrien,Node.cost+1,Node.grabGold, Node.killWumpus)        
                succesors.append(fchild)
                if Node.xPos > 0 :
                    fchild = state('w' ,Node,Node.xPos-1,Node.yPos,'f' ,False, False, False,Node.hasArrow,Node.ArrowOrien,Node.cost+1,Node.grabGold, Node.killWumpus)
                    succesors.append(fchild)
                if Node.hasArrow == True:
                    fchild = state('w' ,Node,Node.xPos,Node.yPos,'a' ,False, False, False,Node.hasArrow,Node.ArrowOrien,Node.cost+1,Node.grabGold, Node.killWumpus)
                    succesors.append(fchild)    
            if Node.orientation == 's':
                fchild = state('e' ,Node,Node.xPos,Node.yPos,'l' ,False, False, False,Node.hasArrow,Node.ArrowOrien,Node.cost+1,Node.grabGold, Node.killWumpus)        
                succesors.append(fchild)
                if Node.yPos > 0 :
                    fchild = state('s' ,Node,Node.xPos,Node.yPos-1,'f' ,False, False, False,Node.hasArrow,Node.ArrowOrien,Node.cost+1,Node.grabGold, Node.killWumpus)
                    succesors.append(fchild)
                fchild = state('w' ,Node,Node.xPos,Node.yPos,'r' ,False, False, False,Node.hasArrow,Node.ArrowOrien,Node.cost+1,Node.grabGold, Node.killWumpus)
                succesors.append(fchild)     
                if Node.hasArrow == True:
                    fchild = state('s' ,Node,Node.xPos,Node.yPos,'a' ,False, False, False,Node.hasArrow,Node.ArrowOrien,Node.cost+1,Node.grabGold, Node.killWumpus)
                    succesors.append(fchild)      
        print 'succesors of ',Node.xPos,Node.yPos,'are : \n'
        for n in succesors:
            print 'x=%d y=%d  o=%s'%(n.xPos,n.yPos,n.orientation)
        print 'out of successor with new length',len(succesors)
        return succesors
    
    def getCellsNeededForDiscovery(self,Nodes):  
        #return Action consisting of indices the nodes to be observed
#        print 'need Discovery'
#        for n in Nodes:
#            print n.xPos,n.yPos
        x=[]
        x.append(len(Nodes))
        for n in Nodes:
            x.append(n.xPos)
            x.append(n.yPos)
#        print 'nodes to Needed for Discovery',x,'\n'
        returnAction=Action()
        returnAction.charArray = ['q']
        returnAction.intArray = x #fill according to partial nodes
        print 'nodes to Needed for Discovery',returnAction.intArray,'\n'
        return returnAction
        
    def agent_start(self,observation):
        print 'start new episode'
#        print observation.intArray
#        print len(self.partialStateNodes)
        self.strategyIndex=self.strategyIndex+1
        self.searchQ=[]
        self.pathToGoalIndex=-1
        returnAction=Action()
        returnAction.charArray = ['q']
        returnAction.intArray = [1, 0, 0]
        self.agenda = 0
        self.partialStateNodes.append(state('n',None, 0,0,'.', False,False, False, True,'x',0,False, False))
#        print len(self.partialStateNodes)
        self.lastAction=copy.deepcopy(returnAction)
        self.lastObservation=copy.deepcopy(observation)
#        print returnAction.intArray
        return returnAction
    
    def remove_expanded(self):
#        print' \n -----------in Compare------------ \n'
#        print 'before searchQ',len(self.searchQ)
#        print 'no. of expanded',len(self.expandedStates)
        for toCompare in self.expandedStates:
            #print 'expanded x %d y %d o %s G %d P %d W %d Ar %d gG %d' % (toCompare.xPos,toCompare.yPos,toCompare.orientation,toCompare.hasGold,toCompare.hasPit,toCompare.hasWumpus,toCompare.hasArrow,toCompare.grabGold)
            for x in self.searchQ:
                #print 'x %d y %d o %s G %d P %d W %d Ar %d gG %d' % (x.xPos,x.yPos,x.orientation,x.hasGold,x.hasPit,x.hasWumpus,x.hasArrow,x.grabGold)
                if x.xPos == toCompare.xPos and x.yPos == toCompare.yPos and x.orientation == toCompare.orientation:
                    if x.hasArrow == toCompare.hasArrow and x.grabGold==toCompare.grabGold:
                        self.searchQ.remove(x)
#                       print '-------------------------------------FOUND REPEATED STATE-----------------------------------------------------'   
        print 'after searchQ',len(self.searchQ)
            
        
             
    def agent_step(self,reward, observation):
        #Generate random action, 0 or 1
#        print '\n In Step'
#        print 'observation Array', observation.intArray,'\n'
        if self.agenda == 0:
            sucNodes = self.updateStateNodes(observation.intArray)
            self.enqueue(sucNodes)
            
            print len(self.searchQ)
            self.remove_expanded()
#            if len(self.expandedStates) >10:
#                print 'STOP'
#                self.searchQ=[]
#            print len(self.expandedStates)
            if self.searchQ == []:
                print 'search queue empty'
                returnAction=Action()
                returnAction.charArray = ['x']
                returnAction.intArray = []
                return returnAction
            
            first = self.searchQ.pop(0)
            self.expandedStates.append(first)
            print 'after append no. of expanded',len(self.expandedStates)                
#            print 'first x=%d y=%d'%(first.xPos,first.yPos)
        
            isGoal = self.Goal(first)
            #print 'is Goal %s'%isGoal
            if isGoal == True:
                self.createPathToGoal(first)
#               print 'inGoal'
                print len(self.pathToGoal)
                for x in self.pathToGoal:
                    print x
                self.agenda = 1
                returnAction=Action()
                returnAction.charArray = ['.']
                returnAction.intArray = []
                return returnAction
            
            self.partialStateNodes=self.getSuccessor(first)
#            print 'length of partialStateNode to be expanded', len(self.partialStateNodes),'\n'
            return self.getCellsNeededForDiscovery(self.partialStateNodes)
        
        if self.agenda == 1:
#            print 'in Agent agenda '
#            for x in self.pathToGoal:
#                print x
            self.pathToGoalIndex = self.pathToGoalIndex + 1
#            print self.pathToGoalIndex
            returnAction=Action()
            returnAction.charArray = self.pathToGoal[self.pathToGoalIndex]
            returnAction.intArray = []
            return returnAction
        print ' will start in new step'
#        self.lastObservation = Observation()
#        self.lastAction=copy.deepcopy(returnAction)
#        self.lastObservation=copy.deepcopy(observation)
#        returnAction=Action()
#        returnAction.charArray = ['q']
#        returnAction.intArray = [12, 0, 0, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6,0, 7, 0, 8 , 0 , 9 , 0 , 10, 0 , 11]
        #return returnAction
#        thisIntAction=self.randGenerator.randint(0,1)
#        returnAction=Action()
#        returnAction.intArray=[thisIntAction]
#        lastAction=copy.deepcopy(returnAction)
#        lastObservation=copy.deepcopy(observation)
#        return returnAction
    
    def agent_end(self,reward):
        pass
    
    def agent_cleanup(self):
        pass
    
    def agent_message(self,inMessage):
        if inMessage=="what is your name?":
            return "my name is skeleton_agent, Python edition!";
        else:
            return "I don't know how to respond to your message";
    

if __name__=="__main__":
    AgentLoader.loadAgent(skeleton_agent())
